use std::sync::atomic::AtomicU16;
use std::sync::atomic::Ordering;
use std::sync::{Arc, RwLock};
/// 计算文章阅读此书的定时任务
/// AtomicI8 原子类型
/// create: job_scheduler
/// 每小时一次
use std::thread;
use std::time::Duration;
use tklog::error;

use tokio::runtime::Runtime;

use sqlx::MySqlPool;

use crate::entity::access_count::AccessCount;
use job_scheduler::{Job, JobScheduler};

pub struct ArticleReadTimeSchedulerJob {
    articls_click_vue: Arc<RwLock<Vec<AtomicU16>>>,
    db_pool: MySqlPool,
}

impl ArticleReadTimeSchedulerJob {
    /// 初始化一个新的 ArticleReadTimeSchedulerJob 实例。
    /// /// Arc<RwLock<Vec<AtomicU16>>> 这么些看起来性能比较低，但是为什么要这么设计呢？
    /// 因为默认缓存了当前前两页的点击记录，如果访问超过了前两页那么就需要向vec插入数据，所以给vec加了RwLock
    ///
    /// 参数:
    /// - default_count_num: 文章数量，默认是 page_size * 2。
    pub fn new(articls_click_vue: Arc<RwLock<Vec<AtomicU16>>>, db_pool: MySqlPool) -> Self {
        ArticleReadTimeSchedulerJob {
            articls_click_vue,
            db_pool,
        }
    }

    pub fn start(&self) {
        let articls_click_vue = Arc::clone(&self.articls_click_vue);
        let db_pool = self.db_pool.clone(); // 克隆数据库连接池
                                            // 创建一个全局的 Tokio 运行时实例以供重用。
        let runtime = Runtime::new().expect("Unable to create Tokio runtime");

        thread::spawn(move || {
            let mut sched = JobScheduler::new();
            sched.add(Job::new("1/10 * * * * *".parse().unwrap(), move || {
                // println!("hello job scheduler.");

                // 使用之前创建的运行时实例。
                runtime.block_on(async {
                    let mut updates: Vec<(usize, i32)> = Vec::new();
                    //write will block the current thread if can get lock.
                    if let Ok(mut vec) = articls_click_vue.write() {
                        for (idx, value) in vec.iter_mut().enumerate() {
                            let click_times: i32 = value.load(Ordering::SeqCst).into();
                            if click_times > 0 {
                                updates.push((idx, click_times));
                                //更新到数据库后，点击数量重新设置为0
                                value.store(0, Ordering::SeqCst);
                            }
                        }
                    };
                    //这段逻辑必须在获取写锁外边，因为update 是一个异步的方法，所以必须等释放了写锁才能执行异步，否则会死锁。
                    for (idx, click_times) in updates {
                        let account_count =
                            AccessCount::new("", idx.try_into().unwrap(), click_times);
                        if let Err(e) = account_count.update(&db_pool).await {
                            error!("Error updating access count: {}", e);
                        }
                    }
                });
            }));

            loop {
                sched.tick();
                thread::sleep(Duration::from_millis(500));
            }
        });
    }
}

#[cfg(test)]
mod test {

    #[test]
    pub fn test_article_read_time_secheduler() {}
}
